#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "=>"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 27

char commandline[LINE_SIZE];
char* argv[ARGC_SIZE];
int last_code = 0;
int qiut = 0;
char pwd[LINE_SIZE];


// 自定义环境变量表
char myenv[LINE_SIZE];  // 可存一个环境变量


const char* getusername()
{
    return getenv("USER");
}

const char* get_hostname()
{
    return getenv("HOSTNAME");
}

// const char* getpwd()
// {
//     return getenv("PWD");
// }
// 此处存疑
void getpwd()
{
    getcwd(pwd, sizeof(pwd));
}

// 交互，获取命令行
void Interact(char* cline, int size)
{
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),get_hostname(),pwd);
    // printf("size:%d\n",size);
    // char* s = fgets(cline,size,stdin);
    // // 这两条语句有疑
    // assert(s);
    // (void)s;

    fgets(cline,size,stdin);
    // "abcd\n\0"
    cline[strlen(cline)-1] = '\0';
    
    // 测试功能
    // printf("%s\n",commandline);
}

// 字符串分割
int splitstring(char* cline, char* _argv[])
{
    int i = 0;
    // strtok使用用法有疑
    argv[i++] = strtok(commandline,DELIM); 
    while(argv[i++] = strtok(NULL, DELIM));
    return i-1;
    
    // 测试功能
    // for(i=0;argv[i]; ++i)
    // {
    //     printf("%d : %s\n",i, argv[i]);
    // }   
}

// 普通命令的执行
void NormalExcute(char* _argv[])
{
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return;
    }
    else if(id == 0){
        // child, 让子进程执行命令
        // exec系列有疑 
        execvp(_argv[0], _argv);
        
        // 检测fork子进程：ps ajx | ......
        // int cnt = 3;
        // while(cnt){
        //     printf("i am child, pid: %d, ppid: %d, cnt: %d\n",getpid(),getppid(), cnt);
        //     cnt--;
        //     sleep(1);
        // }
        exit(EXIT_CODE);
    }
    else{
        int status = 0;
        int rid = waitpid(id, &status, 0);
        if(rid == id){
            last_code = WEXITSTATUS(status);
        }
    }
    
}

// 关于内建命令
int BuildCommand(char* _argv[], int _argc)
{
    if(_argc==2 && strcmp(_argv[0], "cd")==0){
        // chdir, getpwd用法存疑
        // chdir：改变当前目录工作路径
        chdir(_argv[1]);    // 改当前路径为_argv[1]
        
        getpwd();
        sprintf(getenv("PWD"), "%s", pwd);  // 获取当前路径，将其写入环境变量PWD内
        return 1;
    }
    else if(_argc==2 && strcmp(_argv[0], "export")==0){
        strcpy(myenv, _argv[1]);
        putenv(myenv);
        return 1;
    }
    else if(_argc==2 && strcmp(_argv[0], "echo")==0){
        if(strcmp(_argv[1], "$?")==0){
            printf("%d\n",last_code);
            last_code = 0;
        }
        else if(*_argv[1] == '$'){
            // 获取环境变量
            char* val = getenv(_argv[1]+1);
            if(val) printf("%s\n",val);
        }
        else{
            // 单纯的普通打印字符串
            printf("%s\n",_argv[1]);
        }
        return 1;
    }

    // 特殊处理一下 ls
    if(strcmp(_argv[0], "ls")==0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }

    return 0;
}

int main()
{
    while(!qiut){
        // 1.交互问题，获取命令行
        Interact(commandline, sizeof(commandline));

        // 2.字串分割问题："ls -a -l" => "ls" "-a" "-l"
        int argc = splitstring(commandline, argv);
        if(argc==0) continue;

        // 3.指令的判断
        // 内建命令：本质就是shell内部的一个函数
        int n = BuildCommand(argv, argc);
        
        // 4.普通命令的执行
        if(!n) NormalExcute(argv);

    }
    return 0;
}


// 问题：
// 1.strtok函数
// 2.chdir 改：ll /proc/进程PID/cwd 中的值
// 3.getenv / putenv 
// 4.fork的exec*系列



